import MSConfig; MSConfig.init('.')
import sys; sys.path.append('..' + MSConfig.DIRSEP + 'refact-full')
import MSLogger; log = MSLogger.get('wsgi_server')
import ReaderMSDB
import ReaderMSAPI
import MSObject
import MSObjects
import threading
import time

def Database_threadUpdate(isTerminated, objs, threadUpdateTimeout):
    if threadUpdateTimeout == 0: threadUpdateTimeout = 3600

    log.info('Database::threadUpdate: started with timeout(%d)', threadUpdateTimeout)
    ctr = 0
    while not isTerminated.is_set():
        if ctr >= threadUpdateTimeout:
            try:
                objs.load()
            except Exception, err:
                log.error('Database::threadUpdate: %s', str(err))
            ctr = 0
        time.sleep(2)
        ctr += 2
    log.info('Database::threadUpdate: stopped')

def Database_threadSync(isTerminated, objs, reader, threadSyncTimeout):
    if threadSyncTimeout == 0: threadSyncTimeout = 30

    log.info('Database::threadSync: started with timeout(%d) and reader(%s)', threadSyncTimeout, reader.getName())
    while not isTerminated.is_set():
        try:
            ids = []
            objs.lock.acquire()
            try:
                ids = objs.objs.keys()
            finally:
                objs.lock.release()

            for oid in ids:
                objs.lock.acquire()
                try:
                    o = objs.find(oid, True)
                    if o == None: continue
                    o.lock.acquire()
                    try:
                        reader.getEvents_TODAY(o)
                    finally:
                        o.lock.release()
                finally:
                    objs.lock.release()
        except Exception, err:
            log.error('Database::threadSync: %s', str(err))
        time.sleep(threadSyncTimeout)
    log.info('Database::threadSync: stopped')

class Database:
    def __init__(self, path):
        self.objs = MSObjects.MSObjects(path)
        self.isTerminated = threading.Event(); self.isTerminated.clear()
        self.threadUpdate = None
        self.threadSyncMSDB     = None
        self.threadSyncMSAPI    = None

    def setAutoUpdate(self, timeout):
        if timeout < 2: raise RuntimeError('wrong timeout value given')
        self.threadUpdate = threading.Thread(None, Database_threadUpdate, 'DatabaseUpdate', (self.isTerminated, self.objs, timeout))
        self.threadUpdate.start()

    def setAutoSync(self, source, timeout):
        if timeout < 5: raise RuntimeError('wrong timeout value given')
        if source == 'msdb':
            reader = ReaderMSDB.Reader(MSConfig.config.get('msdb','host'),
                                       MSConfig.config.get('msdb','port'),
                                       MSConfig.config.get('msdb','dbname'),
                                       MSConfig.config.get('msdb','user'),
                                       MSConfig.config.get('msdb','pwd'))

            self.threadSyncMSDB = threading.Thread(None, Database_threadSync, 'DatabaseSyncMSDB', (self.isTerminated, self.objs, reader, timeout))
            self.threadSyncMSDB.start()
        if source == 'msapi':
            reader = ReaderMSAPI.Reader(MSConfig.config.get('msapi','url'))
            self.threadSyncMSAPI = threading.Thread(None, Database_threadSync, 'DatabaseSyncMSAPI', (self.isTerminated, self.objs, reader, timeout))
            self.threadSyncMSAPI.start()

    def start(self):
        log.debug('Database::start: ...')
        self.objs.load()
        log.info('Database::start: done')

    def stop(self):
        log.debug('Database::stop: ...')
        self.isTerminated.set()
        if self.threadUpdate:   self.threadUpdate.join()
        if self.threadSyncMSDB: self.threadSyncMSDB.join()
        if self.threadSyncMSAPI: self.threadSyncMSAPI.join()
        self.objs.flush()
        log.info('Database::stop: done')

instance = None
def getInstance():
    global instance
    if instance == None: raise RuntimeError('Database instance is not created')
    return instance

##########################
if __name__ == "__main__":
    import sys
    if len(sys.argv) != 2: print "Usage: %s <storage_root>" % (sys.argv[0])
    g_root = sys.argv[1]

    def case1():
        o = MSObject.MSObject(g_root, 'test_obj')
        o.open()
        o.storage.destroy()

        db = Database(g_root)
        db.start()
        db.setAutoUpdate(10)

        try:
            o = MSObject.MSObject(g_root, 'test_obj')
            o.open()
            o.storage.meta['msId'] = 79
            o.append([1, 2, 3])
            o.flush()

            time.sleep(11)
            if db.objs.find('test_obj') == None: raise RuntimeError('case1: failed 1')
        finally:
            db.stop()

    case1()
